package com.gitee.apanlh.util.base;

import com.gitee.apanlh.exp.UnknownTypeException;
import com.gitee.apanlh.util.reflection.ClassTypeUtils;
import com.gitee.apanlh.util.valid.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**	
 * 	BigDecimal工具类
 * 	#mark 都需要增加默认保留小数位
 *
 * 	@author Pan
 */
public class BigDecimalUtils {
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private BigDecimalUtils() {
		//	不允许外部实例
		super();
	}
	
	/**	
	 * 	截断式
	 * 	<br>默认保留两位小数
	 * 
	 * 	@author Pan
	 * 	@param  value	值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toTruncate(Object value) {
		return toTruncate(value, 2);
	} 
	
	/**
	 * 	值转换BigDecimal 截断式
	 * 	<br>自定义小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@param  precision	保留小数位数
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toTruncate(Object value, int precision) {
		return new BigDecimal(String.valueOf(value)).setScale(precision, RoundingMode.DOWN);
	} 
	
	/**	
	 * 	截断式返回字符串
	 * 	<br>默认保留两位小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@return	String
	 */
	public static String toTruncateStr(Object value) {
		return toTruncate(value).toString();
	} 

	/**	
	 * 	截断式返回字符串
	 * 	<br>默认保留两位小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@param  precision	保留小数位数
	 * 	@return	String
	 */
	public static String toTruncateStr(Object value, int precision) {
		return toTruncate(value, precision).toString();
	} 
	
	/**	
	 * 	保留两位小数
	 * 	<br>移除. (小数点)
	 * 
	 * 	@author Pan
	 *  @param  value		值
	 * 	@return	String
	 */
	public static String toTruncateRemovePoint(Object value) {
		return StringUtils.replace(toTruncate(value).toString(), ".", "");
	}
	
	/**	
	 * 	值转换 
	 * 	<br>小数点后两位如果不为0 则 始终对非零舍弃部分前面的数字加1
	 * 	<br>默认保留两位小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toRoundUp(Object value) {
		return toRoundUp(value, 2);
	}
	
	/**	
	 * 	值转换 
	 * 	<br>小数点后两位如果不为0 则 始终对非零舍弃部分前面的数字加1
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 *  @param  precision	保留小数位数
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toRoundUp(Object value, int precision) {
		return new BigDecimal(String.valueOf(value)).setScale(precision, RoundingMode.UP);
	}
	
	/**	
	 * 	值转换 四舍五入 保留两位小数
	 * 	<br>舍弃的部分如果大于等于5就进位，小于5的直接舍弃
	 * 	<br>默认保留两位小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toHalfUp(Object value) {
		return toHalfUp(value, 2);
	}
	
	/**	
	 * 	值转换 四舍五入 保留两位小数
	 * 	<br>舍弃的部分如果大于等于5就进位，小于5的直接舍弃
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 *  @param  precision	保留小数位数
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toHalfUp(Object value, int precision) {
		return new BigDecimal(String.valueOf(value)).setScale(precision, RoundingMode.HALF_UP);
	}
	
	/**	
	 * 	五舍六入
	 * 	<br>舍弃的部分如果大于5才进位，小于或等于5直接舍弃
	 * 	<br>默认保留两位小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toHalfDown(Object value) {
		return toHalfDown(value, 2);
	}
	
	/**	
	 * 	五舍六入
	 * 	<br>舍弃的部分如果大于5才进位，小于或等于5直接舍弃
	 * 
	 * 	@author Pan
	 * 	@param  value			值
	 * 	@param  precision		保留小数点位数
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toHalfDown(Object value, int precision) {
		return new BigDecimal(String.valueOf(value)).setScale(precision, RoundingMode.HALF_DOWN);
	}
	
	/**	
	 * 	转换BigDecimal
	 * 	<br>截断式
	 * 	<br>保留两位小数
	 * 
	 * 	@author Pan
	 * 	@param  value	值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal toBigDecimal(Object value) {
		return new BigDecimal(toTruncate(value).toString());
	}
	
	/**	
	 * 	值相加 不保留小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@param  absValue	相加值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal plus(Object value, Object absValue) {
		return operator(value, absValue, OperatorEnum.PLUS);
	}
	
	/**
	 * 	值相减 不保留小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@param  minusValue	相减值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal minus(Object value, Object minusValue) {
		return operator(value, minusValue, OperatorEnum.MINUS);
	}
	
	/**
	 * 	值相乘 不保留小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@param  multiplyValue	相乘值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal multiply(Object value, Object multiplyValue) {
		return operator(value, multiplyValue, OperatorEnum.MULTIPLY);
	}
	
	/**
	 * 	值相除 保留两位小数
	 * 	<br>默认计算截断式
	 * 	
	 * 	@author Pan
	 * 	@param  value			值
	 * 	@param  divisionValue	相除值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal division(Object value, Object divisionValue) {
		return operator(value, divisionValue, OperatorEnum.DIVISION);
	}

	/**
	 * 	值相除
	 * 	<br>自定义舍入模式
	 *	<br>默认计算截断式
	 *	
	 * 	@author Pan
	 * 	@param  value 			值
	 * 	@param  minusValue 		除数
	 * 	@param  roundingMode	舍入模式
	 * 	@return	BigDecimal
	 */
	public static BigDecimal division(Object value, Object minusValue, RoundingMode roundingMode) {
		return division(value, minusValue, 2, roundingMode);
	}

	/**
	 * 	值相除 
	 * 	<br>自定义保留小数位数、舍入模式
	 * 	TODO 优化
	 * 	@author Pan
	 * 	@param  value 			值
	 * 	@param  minusValue 		除数
	 * 	@param  precision		保留小数点
	 * 	@param  roundingMode	模式
	 * 	@return	BigDecimal
	 */
	public static BigDecimal division(Object value, Object minusValue, int precision, RoundingMode roundingMode) {
		Assert.isNotEmpty(String.valueOf(value));
		Assert.isNotEmpty(String.valueOf(minusValue));
		
		return new BigDecimal(value.toString()).divide(new BigDecimal(minusValue.toString()), precision, roundingMode);
	}
	
	/**	
	 * 	值相余 
	 * 	<br>不保留小数
	 * 
	 * 	@author Pan
	 * 	@param  value		值
	 * 	@param  moduleValue	相余值
	 * 	@return	BigDecimal
	 */
	public static BigDecimal module(Object value, Object moduleValue) {
		return operator(value, moduleValue, OperatorEnum.MODULE);
	}
	
	/**
	 * 	Object全部转换String在进行对比
	 *  <br>小于对比的compare的对象 小于返回-1 等于返回0 大于返回1
	 * 	
	 * 	@author Pan
	 * 	@param  obj				对象
	 * 	@param  compareObj		对比对象
	 * 	@return	int
	 */
	public static int compare(Object obj, Object compareObj) {
		if (ClassTypeUtils.isString(obj) && ClassTypeUtils.isString(compareObj)) {
			return compare((String) obj, (String) compareObj);
		}
		return compare(toTruncate(obj, 2), toTruncate(compareObj, 2));
	}
	
	/**	
	 * 	BigDecimal 对比
	 * 	<br>小于对比的compare的对象 小于返回-1 等于返回0 大于返回1
	 * 
	 * 	@author Pan
	 * 	@param  obj				对象
	 * 	@param  compareObj		对比对象
	 * 	@return	int	
	 */
	public static int compare(BigDecimal obj, BigDecimal compareObj) {
		return obj.compareTo(compareObj);
	}
	
	/**
	 * 	小于对比的compare的对象 小于返回-1 等于返回0 大于返回1
	 * 
	 * 	@author Pan
	 * 	@param  obj				对象
	 * 	@param  compareObj		对比对象
	 * 	@return	int
	 */
	public static int compare(BigDecimal obj, String compareObj) {
		return obj.compareTo(new BigDecimal(compareObj));
	}
	
	/**
	 * 	小于对比的compare的对象 小于返回-1 等于返回0 大于返回1
	 * 	
	 * 	@author Pan
	 * 	@param  obj				对象
	 * 	@param  compareObj		对比对象
	 * 	@return	int
	 */
	public static int compare(BigDecimal obj, int compareObj) {
		return obj.compareTo(new BigDecimal(compareObj));
	}
	
	/**	
	 * 	小于对比的compare的对象 小于返回-1 等于返回0 大于返回1
	 * 
	 * 	@author Pan	
	 * 	@param  obj				对象
	 * 	@param  compareObj		对比对象
	 * 	@return	int
	 */
	public static int compare(String obj, String compareObj) {
		return new BigDecimal(obj).compareTo(new BigDecimal(compareObj));
	}
	
	/**	
	 * 	小于对比的compare的对象 小于返回-1 等于返回0 大于返回1
	 * 
	 * 	@author Pan
	 * 	@param  obj				对象
	 * 	@param  compareObj		对比对象
	 * 	@return	int
	 */
	public static int compare(String obj, int compareObj) {
		return new BigDecimal(obj).compareTo(new BigDecimal(compareObj));
	}
	
	/**		
	 * 	运算方法
	 * 	<br>除法默认保留四舍五入2位小数
	 * 
	 * 	@author Pan
	 * 	@param  object		参与运算的对象
	 * 	@param  object2		参与运算的对象2
	 *	@param  operator	操作符枚举
	 * 	@return	BigDecimal
	 */
	private static BigDecimal operator(Object object, Object object2, OperatorEnum operator) {
		Assert.isNotNull(object);
		Assert.isNotNull(object2);
		
		BigDecimal bigDecimal = new BigDecimal(String.valueOf(object));
		BigDecimal bigDecimal2 = new BigDecimal(String.valueOf(object2));
		
		switch (operator) {
			case PLUS:
				return bigDecimal.add(bigDecimal2);
			case MINUS:
				return bigDecimal.subtract(bigDecimal2);
			case MULTIPLY:
				return bigDecimal.multiply(bigDecimal2);
			case DIVISION:
				//	默认截断式，保留两位小数
				return bigDecimal.divide(bigDecimal2, 2, RoundingMode.DOWN);
			case MODULE:
				return bigDecimal.remainder(bigDecimal2);
			default:
				throw new UnknownTypeException("operator type error");
		}
	}
}
